package top.mybatisx.mpquery.service;

import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.mybatisx.mpquery.toolkit.Sha1Util;
import top.mybatisx.mpquery.toolkit.SqlUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MPQueryxService {

    private static Logger logger = LoggerFactory.getLogger(MPQueryxService.class);

    // 存储已经创建的 mappedStatementId 映射
    private static final Map<String, MappedStatement> mappedStatementIdMap = new HashMap<>();

    // MybatisPlusQueryServiceAutoConfiguration 自动装配注入
    private static SqlSessionFactory sqlSessionFactory;

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        MPQueryxService.sqlSessionFactory = sqlSessionFactory;
    }

    // 本质的查询
    public static <T> List<T> selectList(String sql, List<Object> values, Class<T> resultType) {
        MappedStatement mappedStatement = findMappedStatement(sql, resultType);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Map<String, Object> params = SqlUtil.turnParamsToMap(values); // 转为占位符格式
        // logger.info("params--->{}", params);
        List<T> objects = sqlSession.selectList(mappedStatement.getId(), params);
        // logger.info("objects--->{}", objects);
        return objects;
    }

    // 本质的查询
    public static List<Map<String, Object>> selectList(String sql, List<Object> values) {
        MappedStatement mappedStatement = findMappedStatement(sql, null);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Map<String, Object> params = SqlUtil.turnParamsToMap(values); // 转为占位符格式
        // logger.info("params--->{}", params);
        List<Map<String, Object>> objects = sqlSession.selectList(mappedStatement.getId(), params);
        // logger.info("objects--->{}", objects);
        return objects;
    }

    private static <T> MappedStatement findMappedStatement(String sql, Class<T> resultType) {
        String mappedStatementId = Sha1Util.sha1(sql + resultType);
        // 1.先查询缓存
        MappedStatement mappedStatement = mappedStatementIdMap.get(mappedStatementId);
        if (mappedStatement != null) {
            return mappedStatement;
        }
        // 2.没有的话创建,并置入缓存
        String mapperClassName = "top.mybatisx.mpquery.mapper.BasexMapper";
        // 这个 resultMapId 要重点注意前缀
        String resultMapId = mapperClassName + "." + mappedStatementId;

        Configuration configuration = sqlSessionFactory.getConfiguration();
        LanguageDriver languageDriver = configuration.getDefaultScriptingLanguageInstance();
        // 处理sql
        int questionMarksCnt = SqlUtil.countQuestionMarks(sql);
        sql = SqlUtil.handleMeta(sql); // 用占位符代替 ?
        logger.info("sql--->{}", sql);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, HashMap.class);


        // 构造参数对象
        List<ParameterMapping> parameterMappings = makeParameterMappings(configuration, questionMarksCnt);
        ParameterMap parameterMap = new ParameterMap.Builder(configuration, resultMapId, HashMap.class, parameterMappings).build();
        configuration.addParameterMap(parameterMap);
        Class type = resultType == null ? HashMap.class : resultType;
        ResultMap resultMap = new ResultMap.Builder(configuration, resultMapId, resultType, new ArrayList<>(), true).build();
        configuration.addResultMap(resultMap);


        MapperBuilderAssistant builderAssistant = new MapperBuilderAssistant(configuration, mapperClassName);
        builderAssistant.setCurrentNamespace(mapperClassName);
        mappedStatement = builderAssistant.addMappedStatement(mappedStatementId, sqlSource, StatementType.PREPARED, SqlCommandType.SELECT,
                null, null, null, null, resultMapId, null,
                null, false, true, false, NoKeyGenerator.INSTANCE, "id", "id",
                configuration.getDatabaseId(), languageDriver, null);
        logger.info("MappedStatement 创建成功:{}", mappedStatementId);
        mappedStatementIdMap.put(mappedStatementId, mappedStatement);
        return mappedStatement;
    }

    private static List<ParameterMapping> makeParameterMappings(Configuration configuration, int questionMarksCnt) {
        List<ParameterMapping> parameterMappings = new ArrayList<>();
        for (int i = 0; i < questionMarksCnt; i++) {
            ParameterMapping pm = new ParameterMapping.Builder(configuration, "p" + i, String.class).build();
            parameterMappings.add(pm);
        }
        return parameterMappings;
    }


}
